with Ada.Strings.Fixed;
with Legend.Roles;
with Legend.Roles.Specials;
with Legend.Traces;
with Legend.Grps;
with Legend.Resources;
with Legend.StrRes;
with Legend.Settings;
with Legend.Story_Engine;
with Legend.Items_Dialog;
with Legend.Timers;
package body Legend.Stages.Rooms is

   package Context_Menus is

      CMD_Cure   : constant := 1;
      CMD_Jiedu  : constant := CMD_Cure + 1;
      CMD_State  : constant := CMD_Jiedu + 1;
      CMD_Item   : constant := CMD_State + 1;
      CMD_Exit   : constant := CMD_Item + 1;

   end Context_Menus;

   -- 子场景信息存储在s1.grp或allsin.grp(原始子场景)中
   -- 每个子场景地图大小均为64x64,
   -- 每个场景由6层构成，每层地图中的格子用一个16位数表示贴图索引或事件索引
   -- 事件索引为FFFF表示无事件
   overriding
   procedure Load_Map (this : in out Room_Type)
   is
   begin
      this.Scenes.Clear;
      if this.Scene.Child_Scene /= No_Scene then
         if not Enter_From_World(All_World.Scenes(this.Scene.Child_Scene)) then
            this.Scenes.Include(this.Scene.Child_Scene);
         end if;
      end if;
      this.Layers := All_Scene_Maps(this.Scene.Id);
      this.Hit_Surface := True;
      Traces.Trace_Stories(this.Stories.all);
      Timers.World_Clock.Add_Timer(Interval(6.0), 0, Id => this.Story_Timer);
   end Load_Map;

   overriding
   procedure Load_Heroes (this : in out Room_Type)
   is
      heroes : Roles.Roles_Access := new Roles.Roles_Type(1);
      me : Legend.Roles.Role_Pointer := new Roles.Specials.Player_Type;
   begin
      heroes.Append(me);
      me.Setup(this'Unrestricted_Access, All_World.Heroes(0)'Access);
      me.Horse_Movies(Resources.Leader_Horse_Movies);
      me.Position(this.Scene.Door);
      me.Speed(Settings.Player.Walk.Speed);
      me.Horse(No_Horse);
      me.Stand;
      this.Hero := me;
      this.Heroes := heroes;
      me.Focus;
   end Load_Heroes;

   function Find_Scene_At (
      this  : in out Room_Type;
      Door  : Point_Type;
      Scene : out Uint16_t
      ) return Boolean
   is
   begin
      if not this.Has_Obstacle_At(Door, this.Hero.Horse) then
         -- only one child scene is supported 
         for id of this.Scenes loop
            if this.Scene.Child_Scene_Entry = Door then
               Scene := id;
               return True;
            end if;
         end loop;
      end if;
      Scene := No_Scene;
      return False;
   end Find_Scene_At;


   procedure On_Timer (
      this          : in out Room_Type;
      Timer         : Hash_Type;
      Count         : Natural;
      Cancel_Bubble : in out Boolean
      )
   is
      n : Natural := 0;
   begin
      if Timer = this.Story_Timer then
         for story of this.Stories.all loop
            if story.Visible = Yes then
               if story.Movie_Frame.First /= No_Picture then
                  Next_Frame(story.Movie_Frame);
                  n := n + 1;
               end if;
            end if;
         end loop;
         if n > 0 then
            this.Canvas.Invalidate;
         end if;
         Cancel_Bubble := True;
         return;
      end if;
      Stage_Type(this).On_Timer(Timer, Count, Cancel_Bubble);
   end On_Timer;

   procedure On_Before_Run (this : in out Room_Type)
   is
      use Ada.Calendar;
   begin
      this.Canvas.Repaint;
      this.Check_Story(this.Hero.Position, sc_Walk);
   end On_Before_Run;

   procedure On_Command (
      this : in out Room_Type;
      Command : Integer
      )
   is
   begin
      case Command is
         when Context_Menus.CMD_Cure =>
            this.On_Stage_Menu_Cure;
         when Context_Menus.CMD_Jiedu =>
            this.On_Stage_Menu_Detoxify;
         when Context_Menus.CMD_Item =>
            this.On_Room_Menu_Item;
         when Context_Menus.CMD_State =>
            this.On_Stage_Menu_State;
         when Context_Menus.CMD_Exit =>
            this.On_Room_Menu_Exit;
         when others => 
            null;
      end case;
   end On_Command;

   function Filter (Which : Item_Type) return Boolean
   is
   begin
      return Which.Id /= Resources.Compass_Id;
   end Filter;

   procedure On_Room_Menu_Item (this : in out Room_Type)
   is
      d : Items_Dialog.Items_Dialog_Type;
      grid : Item_Grid_Type;
   begin
      Story_Engine.Selected_Story_Item := No_Item;
      d.Setup(Filter => Filter'Access);
      d.Run;
      grid := d.Selected;
      if grid.Id = No_Item then
         return;
      end if;
      this.Hero.Use_Item(All_World.Items(grid.Id));
   end On_Room_Menu_Item;

   procedure On_Room_Menu_Exit (this : in out Room_Type)
   is
      p : Point_Type;
   begin
      if not Enter_From_World(this.Scene.all) then
         p := this.Scene.Child_Scene_Entry;
      else
         P := (this.Scene.Exits.X(1), this.Scene.Exits.Y(1)); 
      end if;
      this.Hero.Search_Route(P);
      this.Hero.Start_Walk;
   end On_Room_Menu_Exit;

   procedure Use_Story_Item (this : in out Room_Type; Item : in out Item_Type)
   is
   begin
      Story_Engine.Selected_Story_Item := Item.Id;
      this.Check_Story(this.Hero.Next_Position, sc_Item);
   end Use_Story_Item;

   procedure Finalize (this : in out Room_Type)
   is
   begin
      if this.Story_Timer /= 0 then
         Timers.World_Clock.Delete_Timer(this.Story_Timer);
         if this.Talk_With_Wei then
            Story_Engine.Change_Shop_Position;
         end if;
      end if;
      Stage_Type(this).Finalize;
   end Finalize;

   procedure On_Key_Up (
      this          : in out Room_Type;
      Message       : SDL.Events.Keyboard_Event_t;
      Cancel_Bubble : in out Boolean
      )
   is
   begin
      case Message.Keysym.Sym is
         when SDL.Keysym.K_Space =>
            this.Check_Story(this.Hero.Next_Position, sc_Space);
         when others =>
            Stage_Type(this).On_Key_Up(Message, Cancel_Bubble);
      end case;
   end On_Key_Up;

   procedure On_Passby (this : in out Room_Type)
   is
   begin
      this.Check_Story(this.Hero.Position, sc_Walk);
   end On_Passby;

   procedure Shopping (this : in out Room_Type)
   is
      menu : Menus.Menubar_Pointer;
      mi : Menus.Menuitem_Pointer;
      shop_id : Uint16_t := Get_Shop_By_Scene(this.Scene.Id);
      money : Item_Grid_Type := (Resources.Money_Id, 0);

      procedure Wei (Id : Uint16_t)
      is
         use Resources;
      begin
         this.Say(All_Talks(Id).all, Icon => Weixiaobao_Icon);
      end Wei;
   begin
      if shop_id = No_Shop then
         return;
      end if;
      Wei(StrRes.Shopping.Welcome);
      menu := Menus.Create(this'Unrestricted_Access);
      declare
         shop : Shop_Type renames All_World.Shops(shop_id);
         procedure Buy (Index : Integer)
         is
         begin
            if Index < 0 then
               return;
            end if;
            declare
               Item : Item_Type renames All_World.Items(shop.Item(Index));
            begin
               money.Count := shop.Price(Index);
               if not Story_Engine.Is_Money_Enougth(money.Count) then
                  Wei(StrRes.Shopping.Prompt_Lack_Of_Money);
                  return;
               end if;
               shop.Count(Index) := shop.Count(Index) - 1;
               money.Count := -money.Count;
               Story_Engine.Add_Item_Count(money);
               Wei(StrRes.Shopping.Byebye);
            end;
         end Buy;
      begin
         for i in shop.Item'Range loop
            if shop.Item(i) /= No_Item and shop.Count(i) /= 0 then
               declare
                  Item : Item_Type renames All_World.Items(shop.Item(i));
                  Name : UTF8_String := To_UTF8_Name(item.Name);
                  Price : String := shop.Price(i)'Img;
               begin
                  mi := new Menus.Menuitem_Type;
                  mi.Caption(Name & Price);
                  menu.Add_Item(mi);
                  mi.Id(i);
               end;
            end if;
         end loop;
         menu.Run;
         Buy(menu.Id);
      end;
      Menus.Delete(menu);
      this.Talk_With_Wei := True;
   end Shopping;

   procedure On_Popup_Menu (
      this : in out Room_Type;
      Menubar : in out Menus.Menubar_Pointer
      )
   is
      use Menus;
      use Context_Menus;
      mainmenu : Menubar_Pointer := Menus.Create(this'Unrestricted_Access);

      mi_cure     : Menuitem_Pointer := new Menus.Menuitem_Type;
      mi_jiedu    : Menuitem_Pointer := new Menus.Menuitem_Type;
      mi_state    : Menuitem_Pointer := new Menus.Menuitem_Type;
      mi_item     : Menuitem_Pointer := new Menus.Menuitem_Type;
      mi_go       : Menuitem_Pointer := new Menus.Menuitem_Type;
   begin
      mi_cure.Caption (StrRes.Room_Menus.Cure);
      mi_cure.Id(CMD_Cure);
      mi_jiedu.Caption(StrRes.Room_Menus.Jiedu);
      mi_jiedu.ID(CMD_Jiedu);
      mi_state.Caption(StrRes.Room_Menus.State);
      mi_state.Id(CMD_State);
      mi_item.Caption (StrRes.Room_Menus.Item);
      mi_item.Id(CMD_Item);
      mi_go.Caption(StrRes.Room_Menus.Go);
      mi_go.Id(CMD_Exit);
      mainmenu.Add_Item(mi_cure);
      mainmenu.Add_Item(mi_jiedu);
      mainmenu.Add_Item(mi_state);
      mainmenu.Add_Item(mi_item);
      mainmenu.Add_Item(mi_go);
      Menubar := mainmenu;
   end On_Popup_Menu;

end Legend.Stages.Rooms;
